<?php

/**
 * AweController class file.
 *
 * @author Ricardo Obregón <ricardo@obregon.co>
 * @link http://awecrud.obregon.co/
 * @copyright Copyright &copy; 2012 Ricardo Obregón
 * @license http://awecrud.obregon.org/license/ New BSD License
 */

/**
 * AweController is the base class for the generated controllers.
 *
 * @author Ricardo Obregón <ricardo@obregon.co>
 * @package AweCrud.components
 */
abstract class AweController extends Controller
{

    /**
     * @var string The layout for the controller view.
     */
    public $layout = '//layouts/column2';
    /**
     * @var array Context menu items. This property will be assigned to {@link CMenu::items}.
     */
    public $menu = array();
    /**
     * @var array The breadcrumbs of the current page. The value of this property will
     * be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links}
     * for more details on how to specify this property.
     */
    public $breadcrumbs = array();

    /**
     * PHP getter magic method.
     * This method is overridden so that AR attributes can be accessed like properties.
     * @param string $name property name
     * @return mixed property value
     * @see getAttribute
     */
    public function __get($name)
    {
        $getter = 'get' . ucfirst($name);
        if (method_exists($this, $getter)) {
            return $this->$getter();
        }
        return parent::__get($name);
    }

    /**
     * PHP setter magic method.
     * This method is overridden so that AR attributes can be accessed like properties.
     * @param string $name property name
     * @param mixed $value property value
     */
    public function __set($name, $value)
    {
        $setter = 'set' . ucfirst($name);
        if (method_exists($this, $setter)) {
            return $this->$setter($value);
        }
        parent::__set($name, $value);
    }

    /**
     * Returns the data model based on the primary key or another attribute.
     * This method is designed to work with the values passed via GET.
     * If the data model is not found or there's a malformed key, an
     * HTTP exception will be raised.
     * #MethodTracker
     * This method is based on the gii generated method controller::loadModel, from version 1.1.7 (r3135). Changes:
     * <ul>
     * <li>Support to composite PK.</li>
     * <li>Support to use any attribute (column) name besides the PK.</li>
     * <li>Support to multiple attributes.</li>
     * <li>Automatically detects the PK column names.</li>
     * </ul>
     * @param mixed $key The key or keys of the model to be loaded.
     * If the key is a string or an integer, the method will use the tables' PK if
     * the PK has a single column. If the table has a composite PK and the key
     * has a separator (see below), the method will detect it and use it.
     * <pre>
     * $key = '12-27'; // PK values with separator for tables with composite PK.
     * </pre>
     * If $key is an array, it can be indexed by integers or by attribute (column)
     * names, as for {@link CActiveRecord::findByAttributes}.
     * If the array doesn't have attribute names, as below, the method will use
     * the table composite PK.
     * <pre>
     * $key = array(
     *   12,
     *   27,
     *   ...,
     * );
     * </pre>
     * If the array is indexed by attribute names, as below, the method will use
     * the attribute names to search for and load the model.
     * <pre>
     * $key = array(
     *   'model_id' => 44,
     *      ...,
     * );
     * </pre>
     * Warning: each attribute used should have an index on the database and the set of
     * attributes used should identify only one item on the database (the attributes being
     * ideally part of or multiple unique keys).
     * @param string $modelClass The model class name.
     * @return AweActiveRecord The loaded model.
     * @see AweActiveRecord::pkSeparator
     * @throws CHttpException if there's an invalid request (with code 400) or if the model is not found (with code 404).
     */
    public function loadModel($key, $modelClass=__CLASS__)
    {

        // Get the static model.
        $staticModel = AweActiveRecord::model($modelClass);

        if (is_array($key)) {
            // The key is an array.
            // Check if there are column names indexing the values in the array.
            reset($key);
            if (key($key) === 0) {
                // There are no attribute names.
                // Check if there are multiple PK values. If there's only one, start again using only the value.
                if (count($key) === 1) {
                    return $this->loadModel($key[0], $modelClass);
                }

                // Now we will use the composite PK.
                // Check if the table has composite PK.
                $tablePk = $staticModel->getTableSchema()->primaryKey;
                if (!is_array($tablePk)) {
                    throw new CHttpException(400, Yii::t('AweCrud.messages', 'Your request is invalid.'));
                }

                // Check if there are the correct number of keys.
                if (count($key) !== count($tablePk)) {
                    throw new CHttpException(400, Yii::t('AweCrud.messages', 'Your request is invalid.'));
                }

                // Get an array of PK values indexed by the column names.
                $pk = $staticModel->fillPkColumnNames($key);

                // Then load the model.
                $model = $staticModel->findByPk($pk);
            } else {
                // There are attribute names.
                // Then we load the model now.
                $model = $staticModel->findByAttributes($key);
            }
        } else {
            // The key is not an array.
            // Check if the table has composite PK.
            $tablePk = $staticModel->getTableSchema()->primaryKey;
            if (is_array($tablePk)) {
                // The table has a composite PK.
                // The key must be a string to have a PK separator.
                if (!is_string($key)) {
                    throw new CHttpException(400, Yii::t('AweCrud.messages', 'Your request is invalid.'));
                }

                // There must be a PK separator in the key.
                if (stripos($key, $staticModel->pkSeparator) === false) {
                    throw new CHttpException(400, Yii::t('AweCrud.messages', 'Your request is invalid.'));
                }

                // Generate an array, splitting by the separator.
                $keyValues = explode($staticModel->pkSeparator, $key);

                // Start again using the array.
                return $this->loadModel($keyValues, $modelClass);
            } else {
                // The table has a single PK.
                // Then we load the model now.
                $model = $staticModel->findByPk($key);
            }
        }

        // Check if we have a model.
        if ($model === null) {
            throw new CHttpException(404, Yii::t('AweCrud.messages', 'The requested page does not exist.'));
        }

        return $model;
    }

    public static function validate($models, $attributes = null, $loadInput = true)
    {
        $result = array();
        if (!is_array($models)) {
            $models = array($models);
        }
        foreach ($models as $model) {
            if (is_array($model)) {
                foreach ($model as $key => $submodel) {
                    if ($loadInput && isset($_POST[get_class($submodel)][$key])) {
                        $submodel->attributes = $_POST[get_class($submodel)][$key];
                    }
                    $submodel->validate($attributes);
                    foreach ($submodel->getErrors() as $attribute => $errors) {
                        $result[CHtml::activeId($submodel, '[' . $key . ']' . $attribute)] = $errors;
                    }
                }
            } else {
                if ($loadInput && isset($_POST[get_class($model)])) {
                    $model->attributes = $_POST[get_class($model)];
                }
                $model->validate($attributes);
                foreach ($model->getErrors() as $attribute => $errors) {
                    $result[CHtml::activeId($model, $attribute)] = $errors;
                }
            }
        }
        if (Yii::app()->getRequest()->getIsAjaxRequest()) {
            return function_exists('json_encode') ? json_encode($result) : CJSON::encode($result);
        } else {
            return $result;
        }
    }

    /**
     * Performs the AJAX validation.
     * #MethodTracker
     * This method is based on the gii generated method controller::performAjaxValidation, from version 1.1.7 (r3135). Changes:
     * <ul>
     * <li>Supports multiple models.</li>
     * </ul>
     * @param CModel|array $model The model or array of models to be validated.
     * @param string $form The name of the form. Optional.
     */
    protected function performAjaxValidation($model, $form = null)
    {
        if (Yii::app()->getRequest()->getIsAjaxRequest() && (($form === null) || ($_POST['ajax'] == $form))) {
            echo self::validate($model);
            Yii::app()->end();
        }
    }

    public function render($view, $data = null, $return = false)
    {
        if (isset($_REQUEST['ajax']) or isset($_REQUEST['noUI'])) {
            $output = $this->renderPartial($view, $data, $return);
            //$output = $this->processOutput($output);
            Yii::app()->getClientScript()->render($output);
            $output = $this->processDynamicOutput($output);
            if ($return) {
                return $output;
            } else {
                echo $output;
            }
            return;
        }
        return parent::render($view, $data, $return);
    }

    public function endResponseWithoutWebLogRoute()
    {
        foreach (Yii::app()->log->routes as $route) {
            if ($route instanceof CWebLogRoute) {
                $route->enabled = false;
            }
        }
        Yii::app()->end();
    }

    /**
     * Finds the related primary keys specified in the form POST.
     * Only for HAS_MANY and MANY_MANY relations.
     * @param array $form The post data.
     * @param array $relations A list of model relations in the format returned by {@link CActiveRecord::relations}.
     * @param string $uncheckValue Since Yii 1.1.7, htmlOptions (in {@link CHtml::activeCheckBoxList})
     * has an option named 'uncheckValue'. If you set it to different values than the default value (''), you will
     * need to set the appropriate value to this argument. This method can't be used when 'uncheckValue' is null.
     * @return array An array where the keys are the relation names (string) and the values are arrays with the related model primary keys (int|string) or composite primary keys (array with pk name (string) => pk value (int|string)).
     * Example of returned data:
     * <pre>
     * array(
     *   'categories' => array(1, 4),
     *   'tags' => array(array('id1' => 3, 'id2' => 7), array('id1' => 2, 'id2' => 0)) // composite pks
     * )
     * </pre>
     * An empty array is returned in case there is no related pk data from the post.
     * This data comes directly from the form POST data.
     * @see GxHtml::activeCheckBoxList
     * @throws InvalidArgumentException If uncheckValue is null.
     */
    protected function getRelatedData($form, $relations, $uncheckValue = '')
    {
        if ($uncheckValue === null) {
            throw new InvalidArgumentException(Yii::t(
                'AweCrud.messages',
                'AweCrud cannot handle automatically the POST data if "uncheckValue" is null.'
            ));
        }

        $relatedPk = array();
        foreach ($relations as $relationName => $relationData) {
            if (isset($form[$relationName]) && (($relationData[0] == AweActiveRecord::HAS_MANY) || ($relationData[0] == AweActiveRecord::MANY_MANY))) {
                $relatedPk[$relationName] = $form[$relationName] === $uncheckValue ? null : $form[$relationName];
            }
        }
        return $relatedPk;
    }

}